package com.kpmg.datalake.schedule.utils.ftp;

import com.kpmg.datalake.schedule.exception.FtpException;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
 * @author Alex.zhao [alex.zhao@kpmg.com]
 * @since Created on azhao6 on 2018-08-13 20:26:29
 * FtpClient
 */
public class FtpClient {
    private static final Logger logger = LoggerFactory.getLogger(FtpClient.class);
    private FTPClient ftpCli = null;

    /**
     * 私有化构造函数
     */
    private FtpClient() {
    }

    /**
     * 获取ftp 链接
     *
     * @param host     ip
     * @param port     端口
     * @param userName 用户名
     * @param password 密码
     * @return ftp信息
     */
    public static FtpClient connect(String host, int port, String userName, String password) {
        return new FtpClient().init(host, port, userName, password);
    }

    /**
     * 获取ftp 链接
     *
     * @param host     ip
     * @param port     端口
     * @param username 用户名
     * @param password 密码
     * @return ftp信息
     */
    private FtpClient init(String host, int port, String username, String password) {
        ftpCli = new FTPClient();
        try {
            // 如果采用默认端口，可以使用ftp.connect(url)的方式直接连接FTP服务器
            // 连接FTP服务器
            ftpCli.connect(host, port);
        } catch (Exception e) {
            throw new FtpException("FTP[" + host + ":" + port + "]连接失败!", e);
        }
        if (FTPReply.isPositiveCompletion(ftpCli.getReplyCode())) {
            try {
                ftpCli.login(username, password);
            } catch (Exception e) {
                throw new FtpException("FTP用户[" + username + "]登陆失败!", e);
            }
        } else {
            throw new FtpException("FTP连接出错!");
        }
        try {
            // 设置被动模式
            ftpCli.enterLocalPassiveMode();
            ftpCli.setFileTransferMode(FTPClient.STREAM_TRANSFER_MODE);
            ftpCli.setFileType(FTP.BINARY_FILE_TYPE);
        } catch (Exception e) {
            logger.error("", e);
            throw new FtpException("FTP初始化出错!", e);
        }
        return this;
    }

    /**
     * 上传
     *
     * @param remotePath 上传目录
     * @param localPath  本地目录
     * @return Boolean
     * @throws Exception Exception
     */
    public Boolean uploadFile(String remotePath, String localPath) {
        File file = new File(localPath);
        File[] files = file.listFiles();
        if (files == null || files.length == 0) {
            return true;
        }
        for (File file1 : files) {
            if (!uploadFiles(file1, remotePath)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 递归上传文件
     *
     * @param localeFile 本地文件/目录
     * @param remotePath 上传目录
     * @return Boolean
     * @throws Exception Exception
     */
    public Boolean uploadFiles(File localeFile, String remotePath) {
        FileInputStream fis = null;
        try {
            if (localeFile.isDirectory()) {
                boolean flag = false;
                ftpCli.makeDirectory(localeFile.getName());
                ftpCli.changeWorkingDirectory(localeFile.getName());
                logger.info("[{}]目录", localeFile.getAbsolutePath());
                File[] files = localeFile.listFiles();
                for (File file : files) {
                    if (uploadFiles(file, remotePath + "/" + localeFile.getName())) {
                        flag = true;
                    } else {
                        return false;
                    }
                }
                ftpCli.changeToParentDirectory();
                return flag;
            } else {
                fis = new FileInputStream(localeFile);
                ftpCli.storeFile(localeFile.getName(), fis);
                logger.info("[{}]上传成功!", localeFile.getAbsolutePath());
                return true;
            }
        } catch (Exception e) {
            throw new FtpException("FTP上传[" + localeFile.getAbsolutePath() + "]出错!", e);
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    //do nothing 防御性容错
                }
            }
        }
    }

    /**
     * 下载
     *
     * @param remotePath 下载目录
     * @param localPath  本地目录
     * @return Boolean
     * @throws Exception Exception
     */
    public Boolean downLoadFile(String remotePath, String localPath) {
        try {
            // 转移到FTP服务器目录
            if (ftpCli.changeWorkingDirectory(remotePath)) {
                FTPFile[] files = ftpCli.listFiles();
                for (FTPFile ff : files) {
                    if (!downLoadFile(ff, localPath)) {
                        return false;
                    }
                }
                return files.length > 0;
            }
        } catch (Exception e) {
            logger.error("", e);
            throw new FtpException("FTP文件下载[" + localPath + "]出错!", e);
        }
        return false;
    }

    /**
     * 递归下载文件
     *
     * @param ftpFile   下载文件/目录
     * @param localPath 本地目录
     * @return
     */
    public boolean downLoadFile(FTPFile ftpFile, String localPath) {
        // 当前处理文件本地路径
        String fileLocalPath = localPath + File.separator + ftpFile.getName();
        // down file
        if (ftpFile.isFile()) {
            OutputStream outputStream = null;
            try {
                File localFile = new File(fileLocalPath);
                if (!localFile.getParentFile().exists()) {
                    localFile.getParentFile().mkdirs();
                }
                outputStream = new FileOutputStream(localFile);
                ftpCli.retrieveFile(ftpFile.getName(), outputStream);
                outputStream.flush();
                logger.info("[{}]下载成功!", localFile.getAbsolutePath());
                return true;
            } catch (Exception e) {
                logger.error("", e);
                throw new FtpException("FTP下载[" + fileLocalPath + "]出错!", e);
            } finally {
                try {
                    if (outputStream != null) {
                        outputStream.close();
                    }
                } catch (IOException e) {
                    //do nothing
                    //防御性容错
                }
            }
        } else { // deal dirctory
            File file = new File(fileLocalPath);
            if (!file.exists()) {
                file.mkdirs();
            }
            try {
                // enter relative workdirectory
                if (ftpCli.changeWorkingDirectory(ftpFile.getName())) {
                    logger.info("[{}]目录", file.getAbsolutePath());
                    FTPFile[] files = ftpCli.listFiles();
                    for (FTPFile file1 : files) {
                        downLoadFile(file1, fileLocalPath);
                    }
                    ftpCli.changeToParentDirectory();// return parent
                    // directory
                    return true;
                }
            } catch (Exception e) {
                logger.error("", e);
                throw new FtpException("FTP下载[" + fileLocalPath + "]出错!", e);
            }
        }
        return false;
    }

    /**
     * 关闭ftp 客户端
     *
     * @throws FtpException ftpException
     */
    public void close() {
        try {
            if (ftpCli != null) {
                ftpCli.logout();
            }
        } catch (Exception e) {
            logger.error("", e);
            ftpCli = null;
            throw new FtpException("FTP退出登录出错!", e);
        }
    }

}
